package com.purchase.service.impl;

import com.purchase.entity.Charge;
import com.purchase.entity.GoodsStock;
import com.purchase.mapper.*;
import com.purchase.service.RefundService;
import com.purchase.util.*;
import com.purchase.util.wxpay.TransAmountUtil;
import com.purchase.util.wxpay.WXPayUtil;
import com.purchase.util.wxpay.app.WxPayAESUtilAPP;
import com.purchase.util.wxpay.jsapi.WxPayAESUtil;
import com.purchase.util.wxpay.app.WxpayHandlerAPP;
import com.purchase.util.wxpay.jsapi.WxpayHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class RefundServiceImpl implements RefundService {

    @Autowired
    private WxpayHandler wxpayHandler;

    @Autowired
    private WxpayHandlerAPP wxpayHandlerAPP;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private PayMapper payMapper;

    @Autowired
    private RefundPayMapper refundPayMapper;

    @Autowired
    private RefundOrderMapper refundOrderMapper;

    @Autowired
    private GoodsStockMapper goodsStockMapper;

    @Autowired
    private ChargeMapper chargeMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseForm refund(RequestForm param) {
        ResponseForm result = new ResponseForm();
        Map<String, String> paramMap = (Map<String, String>) param.getData();
        String orderId = null;
        try {
            orderId = paramMap.get("orderId");
            if (!StringUtils.isNotBlank(orderId)) {
                result.setStatus(false);
                result.setMessage("请检查参数");
                log.error("请检查参数");
                return result;
            }

            log.info("订单" + orderId + "开始发起退款");

            Map<String, Object> payOrder = payMapper.getRefundablePayOrderByOrderId(orderId);
            if (payOrder != null) {
                log.debug("获取到可退款流水号={}", payOrder.get("id"));
            } else {
                result.setStatus(false);
                result.setMessage("订单:" + orderId + "不是支付成功状态,无法申请退款");
                log.error("订单:" + orderId + "不是支付成功状态,无法申请退款");
                return result;
            }

            Map<String, Object> oldRefundPayOrder = refundPayMapper.getRefundPayOrderByOrderId(orderId);
            Map<String, Object> oldRefundOrder = refundOrderMapper.getRefundOrderByOrderId(orderId);
            String tradeType = String.valueOf(payOrder.get("trade_type"));
            Map<String, String> map;

            //校验库里是否存在已失败退款流水和退款订单
            if (oldRefundOrder != null && oldRefundPayOrder != null && "F".equals(oldRefundPayOrder.get("refund_status")) && "F".equals(oldRefundOrder.get("refund_order_status"))) {
                map = getOldRefundMap(orderId, payOrder, oldRefundPayOrder, oldRefundOrder, tradeType);
            } else if (oldRefundOrder == null && oldRefundPayOrder == null) {
                map = getNewRefundMap(orderId, payOrder, tradeType);
            } else {
                result.setStatus(false);
                result.setMessage("订单:" + orderId + "无法申请退款");
                log.error("订单:" + orderId + "无法申请退款");
                return result;
            }
            result.setData(map);

        } catch (Exception e) {
            result.setStatus(false);
            result.setMessage("操作错误");
            log.error("申请退款error,orderId=" + orderId, e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

    private Map<String, String> getOldRefundMap(String orderId, Map<String, Object> payOrder, Map<String, Object> oldRefundPayOrder, Map<String, Object> oldRefundOrder, String tradeType) {
        String refundPayOrderId;
        String refundOrderId;
        //1.1判断数据库里是否已有失败退款单号(有退款单号时校验退款订单对应流水状态)

        //组装微信支付退款信息
        Map<String, Object> refundMap = new HashMap<>();
        refundPayOrderId = String.valueOf(oldRefundPayOrder.get("id"));
        refundOrderId = String.valueOf(oldRefundOrder.get("id"));
        log.debug("已存在失败状态的退款流水，退款流水号={}", refundPayOrderId);
        log.debug("已存在失败状态的退款订单，退款订单号={}", refundOrderId);
        refundMap.put("out_refund_no", refundPayOrderId);//商户退款单号
        refundMap.put("transaction_id", payOrder.get("transaction_id"));//微信订单号
        refundMap.put("out_trade_no", payOrder.get("id"));//商户订单号
        refundMap.put("total_fee", payOrder.get("total_fee"));//订单总金额
        refundMap.put("refund_fee", payOrder.get("total_fee"));//退款金额
        refundMap.put("refund_desc", "");//退款原因

        //调用微信支付申请退款
        log.debug("调用微信支付申请退款，退款流水号={}", refundPayOrderId);
        Map<String, String> map = null;
        if ("JSAPI".equals(tradeType)) {
            map = wxpayHandler.refund(refundMap);
        } else if ("APP".equals(tradeType)) {
            map = wxpayHandlerAPP.refund(refundMap);
        }
        log.debug(map.toString());

        //如果申请退款成功（只是申请成功，非退款成功）
        if ("SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
            //修改退款订单状态U
            Map<String, Object> updRefundOrderMap = new HashMap<>();
            updRefundOrderMap.put("id", refundOrderId);
            updRefundOrderMap.put("refundOrderStatus", "U");
            log.debug("修改退款订单状态U，退款单号={}", refundOrderId);
            refundOrderMapper.modifyRefundOrder(updRefundOrderMap);
            //修改退款流水状态U
            Map<String, Object> updRefundPayOrderMap = new HashMap<>();
            updRefundPayOrderMap.put("id", refundPayOrderId);
            updRefundPayOrderMap.put("refundStatus", "U");
            log.debug("修改退款流水状态U，退款流水号={}", refundPayOrderId);
            refundPayMapper.modifyRefundPayOrder(updRefundPayOrderMap);
            //修改订单状态
            Map<String, Object> updOrderMap = new HashMap<>();
            updOrderMap.put("orderId", orderId);
            updOrderMap.put("orderStatus", "RU");//退款中
            log.debug("修改订单状态RU，订单号={}", orderId);
            orderMapper.updateOrderStatusById(updOrderMap);
        } else {
            //修改退款订单状态F
            Map<String, Object> updRefundOrderMap = new HashMap<>();
            updRefundOrderMap.put("id", refundOrderId);
            updRefundOrderMap.put("refundOrderStatus", "F");
            log.debug("修改退款订单状态U，退款单号={}", refundOrderId);
            refundOrderMapper.modifyRefundOrder(updRefundOrderMap);
            //修改退款流水状态F
            Map<String, Object> updRefundPayOrderMap = new HashMap<>();
            updRefundPayOrderMap.put("id", refundPayOrderId);
            updRefundPayOrderMap.put("refundStatus", "F");
            log.debug("修改退款流水状态U，退款流水号={}", refundPayOrderId);
            refundPayMapper.modifyRefundPayOrder(updRefundPayOrderMap);
            //修改订单状态
            Map<String, Object> updOrderMap = new HashMap<>();
            updOrderMap.put("orderId", orderId);
            updOrderMap.put("orderStatus", "RF");//退款失败
            log.debug("修改订单状态RF，订单号={}", orderId);
            orderMapper.updateOrderStatusById(updOrderMap);
        }
        return map;
    }

    private Map<String, String> getNewRefundMap(String orderId, Map<String, Object> payOrder, String tradeType) {
        String refundPayOrderId;
        String refundOrderId;
        //组装微信支付退款信息
        Map<String, Object> refundMap = new HashMap<>();
        //1.2判断是否有退款单号(无退款单号时通过支付单号查询支付流水,生成退款流水)
        log.debug("没有已存在的退款流水，生成新的退款流水和退款订单");
        refundPayOrderId = OrderIdGenerater.generateRefundPayOrderId();
        refundMap.put("out_refund_no", refundPayOrderId);//商户退款单号
        refundMap.put("transaction_id", payOrder.get("transaction_id"));//微信订单号
        refundMap.put("out_trade_no", payOrder.get("id"));//商户订单号
        refundMap.put("total_fee", payOrder.get("total_fee"));//订单总金额
        refundMap.put("refund_fee", payOrder.get("total_fee"));//退款金额
        refundMap.put("refund_desc", "");//退款原因

        //组装退款订单
        Map<String, Object> refundOrderMap = new HashMap<>();
        refundOrderId = OrderIdGenerater.generateRefundOrderId();
        refundOrderMap.put("id", refundOrderId);
        refundOrderMap.put("orderId", orderId);
        refundOrderMap.put("payOrderId", payOrder.get("id"));
        refundOrderMap.put("refundPrice", payOrder.get("total_fee"));
        refundOrderMap.put("refundDesc", "");
        refundOrderMap.put("orderPrice", payOrder.get("total_fee"));
        log.debug("保存退款订单，退款订单号={}", refundOrderId);
        refundOrderMapper.saveRefundOrder(refundOrderMap);

        //组装退款流水
        HashMap<String, Object> refundPayOrderMap = new HashMap<>();
        refundPayOrderMap.put("id", refundPayOrderId);
        refundPayOrderMap.put("refundOrderId", refundOrderId);
        refundPayOrderMap.put("orderId", orderId);
        refundPayOrderMap.put("totalFee", payOrder.get("total_fee"));
        refundPayOrderMap.put("refundTotalFee", payOrder.get("total_fee"));
        log.debug("保存退款流水，退款流水号={}", refundPayOrderId);
        refundPayMapper.saveRefundPayOrder(refundPayOrderMap);

        //调用微信支付申请退款
        log.debug("调用微信支付申请退款，退款流水号={}", refundPayOrderId);
        Map<String, String> map = null;
        if ("JSAPI".equals(tradeType)) {
            map = wxpayHandler.refund(refundMap);
        } else if ("APP".equals(tradeType)) {
            map = wxpayHandlerAPP.refund(refundMap);
        }
        log.debug(map.toString());

        //如果申请退款成功（只是申请成功，非退款成功）
        if ("SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
            //修改订单状态
            Map<String, Object> updOrderMap = new HashMap<>();
            updOrderMap.put("orderId", orderId);
            updOrderMap.put("orderStatus", "RU");//退款中
            log.debug("修改订单状态为退款中，订单号={}", orderId);
            orderMapper.updateOrderStatusById(updOrderMap);
        } else {
            //修改退款订单状态F
            Map<String, Object> updRefundOrderMap = new HashMap<>();
            updRefundOrderMap.put("id", refundOrderId);
            updRefundOrderMap.put("refundOrderStatus", "F");
            log.debug("修改退款订单状态F，退款单号={}", refundOrderId);
            refundOrderMapper.modifyRefundOrder(updRefundOrderMap);
            //修改退款流水状态F
            Map<String, Object> updRefundPayOrderMap = new HashMap<>();
            updRefundPayOrderMap.put("id", refundPayOrderId);
            updRefundPayOrderMap.put("refundStatus", "F");
            updRefundPayOrderMap.put("refundFailDesc", map.get("err_code_des"));
            log.debug("修改退款流水状态F，退款流水号={}", refundPayOrderId);
            refundPayMapper.modifyRefundPayOrder(updRefundPayOrderMap);
            //修改订单状态
            Map<String, Object> updOrderMap = new HashMap<>();
            updOrderMap.put("orderId", orderId);
            updOrderMap.put("orderStatus", "RF");//退款失败
            log.debug("修改订单状态RF，订单号={}", orderId);
            orderMapper.updateOrderStatusById(updOrderMap);
        }
        return map;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String refundNotify(HttpServletRequest request) {
        log.info("接收微信退款结果通知-开始");
        String resXml = "";
        Map resultMap = new HashMap();
        try {
            //接收http请求处理
            String recXml = HttpClient.reciveHttpReq(request, "微信退款结果通知");

            //xml转map
            Map<String, String> recMap = WXPayUtil.xmlToMap(recXml);
            log.debug("xml ot map {}", recMap);
            String reqInfo = recMap.get("req_info");
            String appid = recMap.get("appid");
            String data = null;

            //解密
            if (appid.equals(PropertiesUtil.getValue(PropertiesUtil.WXPATH, "wxpay.appid"))) {
                data = WxPayAESUtil.decryptData(reqInfo);
            } else if (appid.equals(PropertiesUtil.getValue(PropertiesUtil.WXPATH, "wxpay.appid.APP"))) {
                data = WxPayAESUtilAPP.decryptData(reqInfo);
            }

            Map<String, Object> notifyMap = transfer(WXPayUtil.xmlToMap(data));
            String notifyReturnCode = String.valueOf(notifyMap.get("return_code"));
            String notifyReturnMsg = String.valueOf(notifyMap.get("return_msg"));
            String notifyRefundStatus = String.valueOf(notifyMap.get("refund_status"));
            String notifyRefundPayOrderId = String.valueOf(notifyMap.get("out_refund_no"));

            log.debug("获取退款中流水信息");
            Map<String, Object> refundPayOrder = refundPayMapper.getRefundPayOrderById(notifyRefundPayOrderId);
            String orderId = String.valueOf(refundPayOrder.get("order_id"));

            Map<String, Object> order = orderMapper.getOrderByIdAndStoreNo(orderId, null);
            String orderStatus = String.valueOf(order.get("order_status"));

            if (!"RU".equals(orderStatus) && !"RF".equals(orderStatus)) {
                log.error("接收微信退款结果通知error，订单" + orderId + "无对应可退款流水");
                resultMap.put("return_msg", "OK");
                resultMap.put("return_code", "SUCCESS");
                resXml = WXPayUtil.mapToXml(resultMap);
                return resXml;
            }

            String refundOrderId = String.valueOf(refundPayOrder.get("refund_order_id"));

            //修改退款流水状态
            Map<String, Object> updRefundPayMap = new HashMap<>();
            updRefundPayMap.put("id", notifyMap.get("out_refund_no"));
            updRefundPayMap.put("endTime", notifyMap.get("success_time"));
            updRefundPayMap.put("refundRecvAccount", notifyMap.get("refund_recv_accout"));
            updRefundPayMap.put("transactionRefundId", notifyMap.get("refund_id"));
            updRefundPayMap.put("refundStatus", notifyRefundStatus);
            updRefundPayMap.put("refundFailDesc", notifyReturnMsg);
            log.debug("修改退款流水状态，退款流水号={}", updRefundPayMap.get("id"));
            refundPayMapper.modifyRefundPayOrder(updRefundPayMap);

            //修改退款订单状态
            Map<String, Object> updRefundOrderMap = new HashMap<>();
            updRefundOrderMap.put("id", refundOrderId);
            updRefundOrderMap.put("refundOrderStatus", notifyRefundStatus);
            log.debug("修改退款单状态，退款单号={}", updRefundOrderMap.get("id"));
            refundOrderMapper.modifyRefundOrder(updRefundOrderMap);

            //修改订单状态
            Map<String, Object> updOrderMap = new HashMap<>();
            updOrderMap.put("orderId", orderId);
            updOrderMap.put("orderStatus", "S".equals(notifyRefundStatus) ? "RS" : "RF");
            log.debug("修改订单退款状态，订单号={}", updOrderMap.get("orderId"));
            orderMapper.updateOrderStatusById(updOrderMap);

            if ("S".equals(notifyRefundStatus)) {
                //如果通知退款成功，则还原库存（通过订单里的商品数量修改库存数量）
                restoreGoodsStock(orderId);
                log.debug("已还原库存，订单号=" + orderId);
                updateCharge(orderId);
                log.debug("退款更新返现数据，订单号=" + orderId);
            }

            //给微信返回响应报文
            resultMap.put("return_code", "SUCCESS");
            resultMap.put("return_msg", "OK");

        } catch (Exception e) {
            log.error("接收微信退款结果通知error", e);
            resultMap.put("return_code", "FAIL");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        try {
            resXml = WXPayUtil.mapToXml(resultMap);
        } catch (Exception e) {
            log.error("响应微信退款结果通知转换mapToXmlerror", e);
        }
        log.info("接收微信退款结果通知-结束" + resXml);
        return resXml;
    }

    @Override
    public ResponseForm refundAudit(RequestForm param) {
        ResponseForm result = new ResponseForm();
        Map<String, Object> paramMap = (Map<String, Object>) param.getData();
        String orderId = null;
        try {
            orderId = String.valueOf(paramMap.get("orderId"));
            String orderStatus = String.valueOf(paramMap.get("orderStatus"));
            //如果不是取消或审核退款中状态则返回
            if ("AU".equals(orderStatus)) {
                Map<String, Object> order = orderMapper.getOrderByIdAndStoreNo(orderId, null);
                //如果库里订单不是成功待发货状态则返回
                if ("S".equals(order.get("order_status")) && "LW".equals(order.get("logistic_status"))) {
                    orderMapper.updateOrderStatusById(paramMap);
                    return result;
                }
            } else if ("C".equals(orderStatus)) {
                Map<String, Object> order = orderMapper.getOrderByIdAndStoreNo(orderId, null);
                //如果库里订单不是审核中状态则返回
                if ("AU".equals(order.get("order_status"))) {
                    orderMapper.updateOrderStatusById(paramMap);
                    return result;
                }
            }
            result.setStatus(false);
            result.setMessage("操作错误");
            return result;
        } catch (Exception e) {
            result.setStatus(false);
            result.setMessage("操作错误");
            log.error("审核退款error,orderId=" + orderId, e);
        }
        return result;
    }

    @Override
    public ResponseForm refundQuery(RequestForm param) {
        log.info("查询退款 - 开始");
        ResponseForm result = new ResponseForm();
        Map<String, Object> paramMap = (Map<String, Object>) param.getData();
        String orderId = null;
        Map<String, String> map = null;
        try {
            //调用微信支付查询退款
            log.debug("调用微信支付查询退款");
            orderId = String.valueOf(paramMap.get("orderId"));
            String tradeType = String.valueOf(paramMap.get("tradeType"));

            Map<String, Object> payOrderByOrderId = payMapper.getPayOrderByOrderId(orderId);
            paramMap.put("transaction_id", payOrderByOrderId.get("transaction_id"));
            if ("JSAPI".equals(tradeType)) {
                map = wxpayHandler.refundQuery(paramMap);
            } else if ("APP".equals(tradeType)) {
                map = wxpayHandlerAPP.refundQuery(paramMap);
            }
            log.debug("微信支付查询退款返回:{}", map.toString());

            result.setData(map);
        } catch (Exception e) {
            result.setStatus(false);
            result.setMessage(LogInfo.ERROR);
            log.error("查询退款error,orderId=" + orderId, e);
        }
        return result;
    }

    private Map<String, Object> transfer(Map<String, String> dataMap) {
        log.debug("接收到微信退款通知 转换后的 map " + dataMap);
        Map<String, Object> transferMap = new HashMap<>();
        transferMap.put("transaction_id", dataMap.get("transaction_id"));
        transferMap.put("out_trade_no", dataMap.get("out_trade_no"));
        transferMap.put("refund_id", dataMap.get("refund_id"));
        transferMap.put("out_refund_no", dataMap.get("out_refund_no"));
        transferMap.put("settlement_refund_fee", TransAmountUtil.fenToYuan(dataMap.get("settlement_refund_fee")));
        String refundStatus;
        switch (dataMap.get("refund_status")) {
            case "SUCCESS":
                refundStatus = "S";
                break;
            case "CHANGE":
                refundStatus = "F";
                break;
            case "REFUNDCLOSE":
                refundStatus = "F";
                break;
            default:
                refundStatus = "F";
        }
        transferMap.put("refund_status", refundStatus);
        transferMap.put("refund_recv_accout", dataMap.get("refund_recv_accout"));
        transferMap.put("success_time", dataMap.get("success_time"));
        return transferMap;
    }

    private void restoreGoodsStock(String orderId) {
        List<Map<String, Object>> detailList = orderMapper.getOrderDetailListByOrderId(orderId);
        for (Map<String, Object> detail : detailList) {
            int goodsId = (int) detail.get("goods_id");
            String specsName = (String) detail.get("specs_name");
            int goodsNum = (int) detail.get("goods_num");

            try {
                //查询库存
                GoodsStock goodsStock = new GoodsStock();
                goodsStock.setGoodsId(goodsId);
                goodsStock.setSpecsName(specsName);
                List<GoodsStock> goodsStocks = goodsStockMapper.select(goodsStock);
                Integer stockNum = goodsStocks.get(0).getStockNum();

                //更新商品库存
                goodsStock.setStockNum(stockNum + goodsNum);
                Example example = new Example(GoodsStock.class);
                Criteria criteria = example.createCriteria();
                criteria.andEqualTo("goodsId", goodsId);
                criteria.andEqualTo("specsName", specsName);
                goodsStockMapper.updateByExampleSelective(goodsStock, example);
            } catch (Exception e) {
                log.error("还原库存error,orderId=" + orderId, e);
            }
        }
    }

    private void updateCharge(String orderId) {
        try {
            Charge charge = new Charge();
            charge.setOrderStatus("RS");
            Example example = new Example(Charge.class);
            Criteria criteria = example.createCriteria();
            criteria.andEqualTo("orderId", orderId);
            chargeMapper.updateByExampleSelective(charge, example);
        } catch (Exception e) {
            log.error("退款更新返现数据error,orderId=" + orderId, e);
        }
    }


}
